package ru.classbase.dbdumper.postgres

import org.springframework.jdbc.core.namedparam.NamedParameterJdbcTemplate
import org.springframework.stereotype.Component
import ru.classbase.dbdumper.AppProperties
import ru.classbase.dbdumper.SchemaProperties
import ru.classbase.dbdumper.logger
import ru.classbase.dbdumper.model.*
import ru.classbase.dbdumper.tableNameMatch
import javax.sql.DataSource
import kotlin.streams.asSequence

@Component
class PostgresMetaInfoExtractor(
    private val props : AppProperties,
    private val namedJdbc: NamedParameterJdbcTemplate,
    private val dataSource: DataSource,
) {
    private val logger = logger()

    fun getMetaData(schemas: List<SchemaProperties> = listOf()): DbMetaData {

        val database = getDatabaseName()

        val sql = """
            select c.table_schema, c.table_name, c.column_name, c.data_type
            from information_schema.columns c
            inner join information_schema.tables t on (c.table_catalog = t.table_catalog and c.table_schema = t.table_schema and c.table_name = t.table_name)
            where t.table_type = 'BASE TABLE'
              and c.table_catalog = :database 
              and c.table_schema in (:schemas) 
              order by c.table_schema, c.table_name, c.ordinal_position
           """.trimMargin()

        val schemaParam = schemas.map { it.schemaName }

        val result = namedJdbc.queryForStream(sql, mapOf("database" to database, "schemas" to schemaParam)) { rs, _ ->
            ColumnRow(
                rs.getString("table_schema"),
                rs.getString("table_name"),
                rs.getString("column_name"),
                rs.getString("data_type")
            )
        }.asSequence()
            .filter {
                val schemaProps = props.schemas.first { schema -> schema.schemaName == it.schemaName }
                tableNameMatch(it.tableName, schemaProps)
            }
            .groupBy { it.schemaName }
            .mapValues { schema ->
            Schema(schema.key,
                schema.value.groupBy { tb -> tb.tableName }.mapValues { cols ->
                    Table(
                        cols.key,
                        cols.value.associateBy({ it.columnName }, { Column(it.columnName, it.columnDataType.intern()) })
                    )
                }
            )
        }

        return DbMetaData(database, result)
    }

    private fun getDatabaseName(): String {
        return dataSource.connection.use { conn ->
            logger.info("Database name: {}", conn.catalog)
            conn.catalog
        }
    }


}